﻿using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using System.ComponentModel;
using System.Collections.Generic;
using System.Drawing.Design;
using System.Data.Objects.DataClasses;
using System.Diagnostics;
using UtfprLibrary.Configurations.DesignTime;
using UtfprLibrary.ComponentModel;
using UtfprLibrary.EntityFramework.ComponentModel;
using UtfprLibrary.Windows.Forms.ComponentModel;
using UtfprLibrary.ComponentModel.Security;
using UtfprLibrary.Helpers;
using UtfprLibrary.Windows.Forms.Helpers;

namespace UtfprLibrary.Windows.Forms {
    public abstract class UtfprBindingNavigatorBase : ToolStrip, ISupportInitialize, IUtfprControl, IUtfprControlProvider {
        #region Core

        #region ISupportInitialize

        public abstract void BeginInit();
        public abstract void EndInit();

        #endregion

        #region UtfprControlService (IUtfprControl, IUtfprControlProvider)

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool UtfprControlEnabled {
            get { return this.Enabled; }
            set { this.Enabled = value; }
        }

        [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public bool UtfprControlVisible {
            get { return this.Visible; }
            set { this.Visible = value; }
        }

        private IUtfprKeyName _functionOrOperation = null;
        [Category("Security"), Description("If IsFunction is true, loads a Function, otherwise loads an Operation")]
        [Editor("UtfprLibrary.Windows.Forms.ComponentModel.UtfprRestrictionEditor, UtfprLibrary.WindowsForms.Design", typeof(UITypeEditor))]
        public IUtfprKeyName FunctionOrOperationRestriction {
            get { return _functionOrOperation; }
            set { _functionOrOperation = value; }
        }

        private UtfprControlBase _utfprControlBase;
        [Browsable(false)]
        public UtfprControlBase UtfprControlBase {
            get {
                if (_utfprControlBase == null) {
                    _utfprControlBase = new UtfprControlBase(this);
                }
                return _utfprControlBase;
            }
        }

        #endregion
              
        #region Properties
        
        #region NavigationItems

        private UtfprToolStripButtonBase _addNewItem;
        /// <summary>
        /// A ToolStripButton that represents the add button of UtfprBindingNavigator
        /// </summary>
        [Browsable(false)]
        public UtfprToolStripButtonBase AddNewItem {
            get { return _addNewItem; }
            set { _addNewItem = value; }
        }

        private UtfprToolStripButtonBase _deleteItem;
        /// <summary>
        /// A ToolStripButton that represents the delete button of UtfprBindingNavigator
        /// </summary>
        [Browsable(false)]
        public UtfprToolStripButtonBase DeleteItem {
            get { return _deleteItem; }
            set { _deleteItem = value; }
        }

        private UtfprToolStripButtonBase _editItem;
        /// <summary>
        /// A ToolStripButton that represents the edit button of UtfprBindingNavigator
        /// </summary>
        [Browsable(false)]
        public UtfprToolStripButtonBase EditItem {
            get { return _editItem; }
            set { _editItem = value; }
        }

        private UtfprToolStripButtonBase _cancelItem;
        /// <summary>
        /// A ToolStripButton that represents the cancel button of UtfprBindingNavigator
        /// </summary>
        [Browsable(false)]
        public UtfprToolStripButtonBase CancelItem {
            get { return _cancelItem; }
            set { _cancelItem = value; }
        }

        private UtfprToolStripButtonBase _saveItem;
        /// <summary>
        /// A ToolStripButton that represents the save button of UtfprBindingNavigator
        /// </summary>
        [Browsable(false)]
        public UtfprToolStripButtonBase SaveItem {
            get { return _saveItem; }
            set { _saveItem = value; }
        }

        private UtfprToolStripButtonBase _queryItem;
        /// <summary>
        /// A ToolStripButton that represents the query button of UtfprBindingNavigator
        /// </summary>
        [Browsable(false)]
        public UtfprToolStripButtonBase QueryItem {
            get { return _queryItem; }
            set { _queryItem = value; }
        }

        private UtfprToolStripButtonBase _refreshItem;
        /// <summary>
        /// A ToolStripButton that represents the reload button of UtfprBindingNavigator
        /// </summary>
        [Browsable(false)]
        public UtfprToolStripButtonBase RefreshItem {
            get { return _refreshItem; }
            set { _refreshItem = value; }
        }

        private UtfprToolStripButtonBase _duplicateItem;
        /// <summary>
        /// A ToolStripButton that represents the duplicate button of UtfprBindingNavigator
        /// </summary>
        [Browsable(false)]
        public UtfprToolStripButtonBase DuplicateItem {
            get { return _duplicateItem; }
            set { _duplicateItem = value; }
        }

        private UtfprToolStripButtonBase _firstItem;
        /// <summary>
        /// A UtfprToolStripButton that represents the FirstItem
        /// </summary>
        [Browsable(false)]
        public UtfprToolStripButtonBase FirstItem {
            get { return _firstItem; }
            set { _firstItem = value; }
        }

        private UtfprToolStripButtonBase _previousItem;
        /// <summary>
        /// A UtfprToolStripButton that represents the PreviousItem
        /// </summary>
        [Browsable(false)]
        public UtfprToolStripButtonBase PreviousItem {
            get { return _previousItem; }
            set { _previousItem = value; }
        }

        private UtfprToolStripButtonBase _nextItem;
        /// <summary>
        /// A UtfprToolStripButton that represents the NextItem
        /// </summary>
        [Browsable(false)]
        public UtfprToolStripButtonBase NextItem {
            get { return _nextItem; }
            set { _nextItem = value; }
        }

        private UtfprToolStripButtonBase _lastItem;
        /// <summary>
        /// A UtfprToolStripButton that represents the LastItem
        /// </summary>
        [Browsable(false)]
        public UtfprToolStripButtonBase LastItem {
            get { return _lastItem; }
            set { _lastItem = value; }
        }

        private ToolStripLabel _lblCurrent;
        /// <summary>
        /// A ToolStripLabel that represents the current index of UtfprBindingNavigator entities
        /// </summary>
        [Browsable(false)]
        public ToolStripLabel LabelCurrent {
            get { return _lblCurrent; }
            set { _lblCurrent = value; }
        }

        private ToolStripSeparator _sepNav;
        /// <summary>
        /// A ToolStripSeparator that represents the separator between UtfprBindingNavigator buttons and navigation
        /// </summary>
        [Browsable(false)]
        public ToolStripSeparator SeparatorNavigation {
            get { return _sepNav; }
            set { _sepNav = value; }
        }

        #endregion
        
        #region CurrencyManager

        private CurrencyManager _cm;
        [Browsable(false)]
        public CurrencyManager CurrencyManager {
            get { return _cm; }
        }

        /// <summary>
        /// Gets a reference to the list being managed by this navigator.
        /// </summary>
        [Browsable(false)]
        public IBindingList List {
            get { return _cm != null ? _cm.List as IBindingList : null; }
        }

        /// <summary>
        /// Gets the item that is currently selected.
        /// </summary>
        [Browsable(false),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public object CurrentItem {
            get { return _cm != null ? _cm.Current : null; }
        }

        /// <summary>
        /// Gets or sets the index of the item that is currently selected.
        /// </summary>
        [Browsable(false),
        DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
        public int Position {
            get { return _cm != null ? _cm.Position : -1; }
            set { if (_cm != null) _cm.Position = value; }
        }

        #endregion

        #region Mode and State

        private UBNModeEnum _currentMode;
        [Browsable(false)]
        public UBNModeEnum CurrentMode {
            get { return _currentMode; }
            set {
                _currentMode = value;
                OnModeChanged(value);
            }
        }

        private UBNStateEnum _currentState;
        [Browsable(false)]
        public UBNStateEnum CurrentState {
            get { return _currentState; }
            set {
                _currentState = value;
                if (_currentState == UBNStateEnum.Adding) {
                    if (_state != Adding) {
                        _state = Adding;
                    }
                } else if(CurrentState == UBNStateEnum.Navigating) {
                    if (_state != Navigating) {
                        _state = Navigating;
                    }
                } else if (CurrentState == UBNStateEnum.Searching) {
                    if (_state != Searching) {
                        _state = Searching;
                    }
                } else if (CurrentState == UBNStateEnum.Updating) {
                    if (_state != Updating) {
                        _state = Updating;
                    }
                }

                OnStateChanged(value);
            }
        }

        private UBNState _state;
        [Browsable(false)]
        public UBNState State {
            get { return _state; }
            protected set {
                _state = value;

                if (_state == Adding) {
                    CurrentState = UBNStateEnum.Adding;
                } else if (_state == Navigating) {
                    CurrentState = UBNStateEnum.Navigating;
                } else if (_state == Searching) {
                    CurrentState = UBNStateEnum.Searching;
                } else if (_state == Updating) {
                    CurrentState = UBNStateEnum.Updating;
                }
            }
        }

        private UBNState _adding;
        internal UBNState Adding {
            get { return _adding; }
        }

        private UBNState _navigating;
        internal UBNState Navigating {
            get { return _navigating; }
        }

        private UBNState _updating;
        internal UBNState Updating {
            get { return _updating; }
        }

        private UBNState _searching;
        internal UBNState Searching {
            get { return _searching; }
        }

        #endregion

        #region Data

        private object _dataSource;
        /// <summary>
        /// Gets or sets the data source for this navigator.
        /// </summary>
        [DefaultValue(null), AttributeProvider(typeof(IListSource))]
        [Category("Data")]
        [Description("Data Source")]
        public object DataSource {
            get { return _dataSource; }
            set {
                _dataSource = value;
                updateCurrencyManager();
            }
        }

        private string _dataMember = string.Empty;
        /// <summary>
        /// Gets or sets the specific list in a <see cref="DataSource"/> object that the navigator should display.
        /// </summary>
        [Category("Data")]
        [Description("Data Member")]
        [DefaultValue("")]
        [Editor("System.Windows.Forms.Design.DataMemberListEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
        public string DataMember {
            get { return _dataMember; }
            set {
                _dataMember = value;
                updateCurrencyManager();
            }
        }

        [Category("Data")]
        [Description("UtfprBindingNavigator Source.")]
        [TypeConverter(typeof(UtfprBindingNavigatorConverter))]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public UtfprBindingNavigatorBase NavigatorSource { get; set; }

        [Category("Data")]
        [Description("Fetch Mode")]
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public FetchModeEnum FetchMode { get; set; }

        protected IEntityDataSource EntityDataSource {
            get {
                var rootSource = UtfprBindingNavigatorBaseHelper.findRootSourceNavigator(this);
                IEntityDataSource ds = null;
                var bs = rootSource._dataSource as BindingSource;
                //var bs = _dataSource as BindingSource;

                if (bs != null) {
                    //ds = bs.DataSource as IEntityDataSource;
                    var provider = bs.DataSource as IEntityDataSourceProvider;
                    if (provider != null) {
                        ds = provider.EntityDataSource;
                    } else {
                        bs = bs.DataSource as BindingSource;
                        if (bs != null) {
                            provider = bs.DataSource as IEntityDataSourceProvider;
                            if (provider != null) {
                                ds = provider.EntityDataSource;
                            }
                        }
                    }
                } else {
                    ds = rootSource._dataSource as IEntityDataSource;
                }

                return ds;
            }
        }

        #endregion
        
        #region Forms

        private UtfprFormCrudBase _utfprCrud;
        /// <summary>
        /// Crud Form
        /// </summary>
        protected UtfprFormCrudBase UtfprCrud {
            get {
                if (_utfprCrud == null) {
                    _utfprCrud = UtfprBindingNavigatorBaseHelper.getUtfprCrud(this);
                }
                return _utfprCrud;
            }
        }
      
        private UtfprFormQueryBase _utfprQuery;
        /// <summary>
        /// Query Form.
        /// </summary>
        protected UtfprFormQueryBase UtfprQuery {
            get {
                if (_utfprQuery == null) {
                    _utfprQuery = UtfprBindingNavigatorBaseHelper.getUtfprQuery(UtfprCrud);
                }
                return _utfprQuery;
            }
        }

        #endregion

        #region Behavior

        private bool _showBtnNav;
        /// <summary>
        /// Gets or sets whether the control should display the navigation buttons.
        /// </summary>
        [Category("Behavior")]
        public bool ShowNavigationButtons {
            get { return _showBtnNav; }
            set {
                _showBtnNav =
                    _firstItem.Visible =
                    _previousItem.Visible =
                    _nextItem.Visible =
                    _lblCurrent.Visible =
                    _lastItem.Visible =
                    _sepNav.Visible = value;
            }
        }

        #endregion

        #region Layout

        protected UtfprBindingNavigatorBaseIcons BaseIcons {
            get { return UtfprApplication.Instance.UtfprBindingNavigatorIconsDefault; }
        }

        #endregion

        #region Utility

        private List<UtfprBindingNavigatorBase> _ubnDetails = new List<UtfprBindingNavigatorBase>();
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        [Browsable(false)]
        public List<UtfprBindingNavigatorBase> UBNDetails {
            get { return _ubnDetails; }
        }

        #endregion

        #endregion

        #region Events

        /// <summary>
        /// Occurs when a data error is fired.
        /// </summary>
        [Category("Data"), Description("Occurs when a data error is fired.")]
        public event EventHandler<DataErrorEventArgs> DataError;

        /// <summary>
        /// Raises the <see cref="DataError"/> event.
        /// </summary>
        /// <param name="e"><see cref="DataErrorEventArgs"/> that contains the event parameters.</param>
        protected virtual void OnDataError(DataErrorEventArgs e) {
            if (DataError != null) {
                DataError(this, e);
            }

            if (!e.Handled) {
                throw e.Exception;
            }
        }

        /// <summary>
        /// Occurs when the current item changes.
        /// </summary>
        [Category("Property Changed"), Description("Occurs when the current item changes.")]
        public event EventHandler PositionChanged;

        /// <summary>
        /// Raises the <see cref="PositionChanged"/> event.
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnPositionChanged(EventArgs e) {
            if (PositionChanged != null)
                PositionChanged(this, e);
        }

        /// <summary>
        /// Occurs when the list changes.
        /// </summary>
        [Category("Property Changed"), Description("Occurs when the list changes.")]
        public event ListChangedEventHandler ListChanged;

        /// <summary>
        /// Raises the <see cref="ListChanged"/> event.
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnListChanged(ListChangedEventArgs e) {
            if (ListChanged != null)
                ListChanged(this, e);
        }

        /// <summary>
        /// Event raised when the state (Adding, Navigating, Searching, Updating) changes.
        /// </summary>
        [Category("Property Changed"), Description("Event raised when the state (Adding, Navigating, Searching, Updating) changes.")]
        public event StateChangedEventHandler StateChanged;

        /// <summary>
        /// Raises the <see cref="StateChanged"/> event.
        /// </summary>
        /// <param name="e"><see cref="CancelEventArgs"/> that contains the event parameters.</param>
        protected virtual void OnStateChanged(UBNStateEnum state) {
            if (StateChanged != null) {
                StateChanged(this, new StateChangedEventArgs(state));
            }
        }

        /// <summary>
        /// Event raised when the mode (Master, Details, Unchanged) changes.
        /// </summary>
        [Category("Property Changed"), Description("Event raised when the mode (Master, Details, Unchanged) changes.")]
        public event ModeChangedEventHandler ModeChanged;

        /// <summary>
        /// Raises the <see cref="ModeChanged"/> event.
        /// </summary>
        /// <param name="e"><see cref="CancelEventArgs"/> that contains the event parameters.</param>
        protected virtual void OnModeChanged(UBNModeEnum mode) {
            if (ModeChanged != null) {
                ModeChanged(this, new ModeChangedEventArgs(mode));
            }
        }

        /// <summary>
        /// Occurs before a new item is added to the list.
        /// </summary>
        [Category("Action"), Description("Occurs before a new item is added to the list.")]
        public event CancelEventHandler AddingNew;

        /// <summary>
        /// Raises the <see cref="AddingNew"/> event.
        /// </summary>
        /// <param name="e"><see cref="CancelEventArgs"/> that contains the event parameters.</param>
        protected virtual void OnAddingNew(CancelEventArgs e) {
            if (AddingNew != null)
                AddingNew(this, e);
        }

        /// <summary>
        /// Occurs after a new item is added to the list.
        /// </summary>
        [Category("Action"), Description("Occurs after a new item is added to the list.")]
        public event EventHandler AddedNew;

        /// <summary>
        /// Raises the <see cref="AddedNew"/> event.
        /// </summary>
        /// <param name="e"><see cref="EventArgs"/> that contains the event parameters.</param>
        protected virtual void OnAddedNew(EventArgs e) {
            if (AddedNew != null)
                AddedNew(this, e);
        }

        /// <summary>
        /// Occurs before an item is removed from the list.
        /// </summary>
        [Category("Action"), Description("Occurs before an item is removed from the list.")]
        public event CurrentItemEventHandler RemovingItem;

        /// <summary>
        /// Raises the <see cref="RemovingItem"/> event.
        /// </summary>
        /// <param name="e"><see cref="CancelEventArgs"/> that contains the event parameters.</param>
        protected virtual void OnRemovingItem(CurrentItemEventArgs e) {
            if (RemovingItem != null)
                RemovingItem(this, e);
        }

        /// <summary>
        /// Occurs after an item is removed from the list.
        /// </summary>
        [Category("Action"), Description("Occurs after an item is removed from the list.")]
        public event CurrentItemEventHandler RemovedItem;

        /// <summary>
        /// Raises the <see cref="RemovedItem"/> event.
        /// </summary>
        /// <param name="e"><see cref="EventArgs"/> that contains the event parameters.</param>
        protected virtual void OnRemovedItem(CurrentItemEventArgs e) {
            if (RemovedItem != null)
                RemovedItem(this, e);
        }

        /// <summary>
        /// Occurs before an item is queried.
        /// </summary>
        [Category("Action"), Description("Occurs before an item is queried.")]
        public event CancelEventHandler Querying;

        /// <summary>
        /// Raises the <see cref="Querying"/> event.
        /// </summary>
        /// <param name="e"><see cref="EventArgs"/> that contains the event parameters.</param>
        protected virtual void OnQuerying(CancelEventArgs e) {
            if (Querying != null)
                Querying(this, e);
        }

        /// <summary>
        /// Occurs after an item is queried.
        /// </summary>
        [Category("Action"), Description("Occurs after an item is queried.")]
        public event EventHandler Queried;

        /// <summary>
        /// Raises the <see cref="Queried"/> event.
        /// </summary>
        /// <param name="e"><see cref="EventArgs"/> that contains the event parameters.</param>
        protected virtual void OnQueried(EventArgs e) {
            if (Queried != null)
                Queried(this, e);
        }

        /// <summary>
        /// Occurs before an item is saved.
        /// </summary>
        [Category("Action"), Description("Occurs before an item is saved.")]
        public event CancelEventHandler Saving;

        /// <summary>
        /// Raises the <see cref="Saving"/> event.
        /// </summary>
        /// <param name="e"><see cref="EventArgs"/> that contains the event parameters.</param>
        protected virtual void OnSaving(CancelEventArgs e) {
            if (Saving != null)
                Saving(this, e);
        }

        /// <summary>
        /// Occurs after an item is saved.
        /// </summary>
        [Category("Action"), Description("Occurs after an item is saved.")]
        public event EventHandler Saved;

        /// <summary>
        /// Raises the <see cref="Saved"/> event.
        /// </summary>
        /// <param name="e"><see cref="EventArgs"/> that contains the event parameters.</param>
        protected virtual void OnSaved(EventArgs e) {
            if (Saved != null)
                Saved(this, e);
        }

        /// <summary>
        /// Occurs before an item is canceled.
        /// </summary>
        [Category("Action"), Description("Occurs before an item is canceled.")]
        public event CancelEventHandler Canceling;

        /// <summary>
        /// Raises the <see cref="Canceling"/> event.
        /// </summary>
        /// <param name="e"><see cref="EventArgs"/> that contains the event parameters.</param>
        protected virtual void OnCanceling(CancelEventArgs e) {
            if (Canceling != null)
                Canceling(this, e);
        }

        /// <summary>
        /// Occurs after an item is canceled.
        /// </summary>
        [Category("Action"), Description("Occurs after an item is canceled.")]
        public event EventHandler Canceled;

        /// <summary>
        /// Raises the <see cref="Canceled"/> event.
        /// </summary>
        /// <param name="e"><see cref="EventArgs"/> that contains the event parameters.</param>
        protected virtual void OnCanceled(EventArgs e) {
            if (Canceled != null)
                Canceled(this, e);
        }

        /// <summary>
        /// Occurs before an item is deleted.
        /// </summary>
        [Category("Action"), Description("Occurs before an item is deleted.")]
        public event CancelEventHandler Deleting;

        /// <summary>
        /// Raises the <see cref="Deleting"/> event.
        /// </summary>
        /// <param name="e"><see cref="EventArgs"/> that contains the event parameters.</param>
        protected virtual void OnDeleting(CancelEventArgs e) {
            if (Deleting != null)
                Deleting(this, e);
        }
        
        /// <summary>
        /// Occurs after an item is deleted.
        /// </summary>
        [Category("Action"), Description("Occurs after an item deleted.")]
        public event EventHandler Deleted;

        /// <summary>
        /// Raises the <see cref="Deleted"/> event.
        /// </summary>
        /// <param name="e"><see cref="EventArgs"/> that contains the event parameters.</param>
        protected virtual void OnDeleted(EventArgs e) {
            if (Deleted != null)
                Deleted(this, e);
        }

        /// <summary>
        /// Occurs before an item is edited.
        /// </summary>
        [Category("Action"), Description("Occurs before an item edited.")]
        public event CancelEventHandler Editing;

        /// <summary>
        /// Raises the <see cref="Editing"/> event.
        /// </summary>
        /// <param name="e"><see cref="EventArgs"/> that contains the event parameters.</param>
        protected virtual void OnEditing(CancelEventArgs e) {
            if (Editing != null)
                Editing(this, e);
        }

        /// <summary>
        /// Occurs after an item is edited.
        /// </summary>
        [Category("Action"), Description("Occurs after an item is edited.")]
        public event EventHandler Edited;

        /// <summary>
        /// Raises the <see cref="Edited"/> event.
        /// </summary>
        /// <param name="e"><see cref="EventArgs"/> that contains the event parameters.</param>
        protected virtual void OnEdited(EventArgs e) {
            if (Edited != null)
                Edited(this, e);
        }

        /// <summary>
        /// Occurs before an item is refreshed.
        /// </summary>
        [Category("Action"), Description("Occurs before an item is refreshed.")]
        public event CancelEventHandler Refreshing;

        /// <summary>
        /// Raises the <see cref="Refreshing"/> event.
        /// </summary>
        /// <param name="e"><see cref="EventArgs"/> that contains the event parameters.</param>
        protected virtual void OnRefreshing(CancelEventArgs e) {
            if (Refreshing != null)
                Refreshing(this, e);
        }

        /// <summary>
        /// Occurs after an item is refreshed.
        /// </summary>
        [Category("Action"), Description("Occurs after an item is refreshed.")]
        public event EventHandler Refreshed;

        /// <summary>
        /// Raises the <see cref="Refreshed"/> event.
        /// </summary>
        /// <param name="e"><see cref="EventArgs"/> that contains the event parameters.</param>
        protected virtual void OnRefreshed(EventArgs e) {
            if (Refreshed != null)
                Refreshed(this, e);
        }

        /// <summary>
        /// Occurs before an item is duplicated.
        /// </summary>
        [Category("Action"), Description("Occurs befor an item is duplicated.")]
        public event CancelEventHandler Duplicating;

        /// <summary>
        /// Raises the <see cref="Duplicating"/> event.
        /// </summary>
        /// <param name="e"><see cref="EventArgs"/> that contains the event parameters.</param>
        protected virtual void OnDuplicating(CancelEventArgs e) {
            if (Duplicating != null)
                Duplicating(this, e);
        }

        /// <summary>
        /// Occurs after an item is duplicated.
        /// </summary>
        [Category("Action"), Description("Occurs after an item is duplicated.")]
        public event EventHandler Duplicated;

        /// <summary>
        /// Raises the <see cref="Duplicated"/> event.
        /// </summary>
        /// <param name="e"><see cref="EventArgs"/> that contains the event parameters.</param>
        protected virtual void OnDuplicated(EventArgs e) {
            if (Duplicated != null)
                Duplicated(this, e);
        }

        #endregion

        #region EventHandlers

        internal void _mode_Changed(object sender, ModeChangedEventArgs e) {
            if (e.Mode == UBNModeEnum.Master) {
                AddNewItem.Enabled = true;
                DeleteItem.Enabled = false;
                EditItem.Enabled = false;
                CancelItem.Enabled = false;
                SaveItem.Enabled = false;
                QueryItem.Enabled = true;
                RefreshItem.Enabled = false;
                DuplicateItem.Enabled = false;
                ModeChanged -= _mode_Changed;
            } else if (e.Mode == UBNModeEnum.Details) {
                UtfprControlEnabled = false;
                
                AddNewItem.Enabled = true;
                DeleteItem.Enabled = false;
                EditItem.Enabled = false;
                CancelItem.Enabled = false;
                SaveItem.Enabled = false;
                
                //Dock = DockStyle.None;
                foreach (ToolStripItem item in Items) {
                    item.ImageScaling = ToolStripItemImageScaling.SizeToFit;
                }
                QueryItem.Visible = false;
                RefreshItem.Visible = false;
                DuplicateItem.Visible = false;

                ModeChanged -= _mode_Changed;
            }
        }

        internal void _cm_PositionChanged(object sender, EventArgs e) {
            try {
                updateNavigationUI();
                OnPositionChanged(e);
                Debug.WriteLine("Done. PositionChanged.");
            } catch (Exception ex) {
                OnDataError(new DataErrorEventArgs(ex));
            }
        }

        internal void _cm_ListChanged(object sender, ListChangedEventArgs e) {
            try {
                if (e.ListChangedType != ListChangedType.ItemChanged) {
                    updateNavigationUI();
                }
                OnListChanged(e);
                Debug.WriteLine("Done. ListChanged.");
            } catch (Exception ex) {
                OnDataError(new DataErrorEventArgs(ex));
            }
        }

        #endregion

        #region Methods

        #region OverrideMethods

        /// <summary>
        /// Before the UtfprBindingNavigator is destroyed, we check if a Master Navigator exists, and try to remove the details from it.
        /// TODO: Check the integrity of this method, trying to fix the possible issues.
        /// </summary>
        protected override void OnHandleDestroyed(EventArgs e) {
            base.OnHandleDestroyed(e);
            try {
                neutralDestoyingHandleBehavior();
            } catch (Exception ex) {
                throw new ArgumentException(ex.Message);
            }
        }

        /// <summary>
        /// Update the internal CurrencyManager when the BindingContext changes.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnBindingContextChanged(EventArgs e) {
            base.OnBindingContextChanged(e);
            try {
                updateCurrencyManager();
            } catch (Exception ex) {
                throw new ArgumentException(ex.Message);
            }
        }

        /// <summary>
        /// Gets or sets how the control is docked in the parent container.
        /// </summary>
        [DefaultValue(DockStyle.Top)]
        public override DockStyle Dock {
            get { return base.Dock; }
            set { base.Dock = value; }
        }

        #endregion

        #region InternalMethods

        /// <summary>
        /// TODO
        /// </summary>
        internal void neutralDestoyingHandleBehavior() {
            if (this.CurrentMode == UBNModeEnum.Master) {
                UtfprBindingNavigatorBaseHelper.removeFromNavigatorSources(this);
            } else if (this.CurrentMode == UBNModeEnum.Details) {
                UtfprBindingNavigatorBaseHelper.removeFromRootSourceNavigator(this);
            }
        }
        
        #endregion

        #region ProtectedMethods


        protected void updateNavigationUI() {
            this.Invoke(new MethodInvoker(delegate {
                if (_cm != null) {
                    _lblCurrent.Text = string.Format("{0} of {1}", _cm.Position + 1, _cm.Count);
                    _firstItem.Enabled = _previousItem.Enabled = _cm.Position > 0 && CurrentState != UBNStateEnum.Updating && CurrentState != UBNStateEnum.Adding;
                    _lastItem.Enabled = _nextItem.Enabled = _cm.Position < _cm.Count - 1 && CurrentState != UBNStateEnum.Updating && CurrentState != UBNStateEnum.Adding;
                } else {
                    _firstItem.Enabled = _previousItem.Enabled = false;
                    _lastItem.Enabled = _nextItem.Enabled = false;
                }
            }));
        }

        // update the currency manager to handle a new DataSource, DataMember, or BindingContext
        protected void updateCurrencyManager() {
            // disconnect old
            if (_cm != null) {
                _cm.PositionChanged -= _cm_PositionChanged;
                _cm.ListChanged -= _cm_ListChanged;
            }

            // get new currency manager
            _cm = null;
            if (DataSource != null && Parent != null && BindingContext != null) {
                try {
                    _cm = BindingContext[DataSource, DataMember.Split('.').Last()] as CurrencyManager;
                } catch { }
            }

            // connect new
            if (_cm != null) {
                _cm.PositionChanged += _cm_PositionChanged;
                _cm.ListChanged += _cm_ListChanged;
            }

            // show UI
            updateNavigationUI();
        }

        /// <summary>
        /// TODO
        /// </summary>
        protected void updateStatusStrip() {
            if (UtfprCrud != null) {
                this.Invoke(new MethodInvoker(delegate {
                    UtfprCrud.UpdateStatusStrip(StateHelper.GetDescription(this.CurrentState));
                }));
            }
        }

        /// <summary>
        /// TODO
        /// </summary>
        protected void updateStatusStrip(string message) {
            if (UtfprCrud != null) {
                this.Invoke(new MethodInvoker(delegate {
                    UtfprCrud.UpdateStatusStrip(message);
                }));
            }
        }

        protected void setupCustomIconsAtDesignTime() {
            AppDomain domain = null;
            Assembly assembly = null;

            try {
                //var file = Directory.GetFiles(UtfprSettings.Settings.ActiveProjectDebugPath, "*.EXE", SearchOption.AllDirectories).Single(f => f.EndsWith(".vshost.exe"));
                var file = Directory.GetFiles(UtfprSettings.Settings.ActiveProjectDebugPath, "*.EXE", SearchOption.AllDirectories).FirstOrDefault();

                if (file != null) {
                    domain = AppDomainHelper.CreateDomain(UtfprSettings.Settings.ActiveProjectDebugPath);
                    assembly = domain.Load(AssemblyName.GetAssemblyName(file));

                    //Throws and InvalidOperationException if more than one
                    Type iconBaseType = assembly.GetTypes()
                        .Single(t => t.BaseType == typeof(UtfprBindingNavigatorBaseIcons));

                    UtfprApplication.Instance._utfprBindingNavigatorIconsDefault =
                        (UtfprBindingNavigatorBaseIcons)assembly.CreateInstance(iconBaseType.FullName);

                    //var setUtfprBindingNavigatorIconsDefaultMethod =
                    //    UtfprApplication.Instance.GetType().GetMethod("SetUtfprBindingNavigatorIconsDefault");
                    //var obj = setUtfprBindingNavigatorIconsDefaultMethod
                    //    .MakeGenericMethod(new Type[] { iconBaseType });
                    //obj.Invoke(UtfprApplication.Instance, null);
                }
            } catch {
                /* Do Nothing */
            } finally {
                if (domain != null) {
                    AppDomain.Unload(domain);
                }
            }
        }

        /// <summary>
        /// TODO: 
        /// </summary>
        protected void createStandartStates() {
            _adding = new Adding(this);
            _navigating = new Navigating(this);
            _updating = new Updating(this);
            _searching = new Searching(this);
        }

        /// <summary>
        /// TODO
        /// </summary>
        protected void setInitialState() {
            State = _navigating;
            ModeChanged += _mode_Changed;
        }

        #endregion

        #endregion

        #endregion

        #region ToOverride

        #region ActionsToOverride

        public abstract void AddNew(CancelEventArgs ce);
        public abstract void Edit(CancelEventArgs ce);
        public abstract void Save(CancelEventArgs ce);
        public abstract void Delete(CancelEventArgs ce);
        public abstract void Cancel(CancelEventArgs ce);
        public abstract void Refresh(CancelEventArgs ce);
        public abstract void Duplicate(CancelEventArgs ce);
        public abstract void Query(CancelEventArgs ce);
        public abstract void QueryById(object id);

        #endregion

        #region PropertiesToOverride

        public abstract string KeyMember { get; set; }

        public abstract IUtfprRelatedEndDescription[] RelatedEndDescriptions { get; set; }
        
        #endregion 

        #endregion
    }
}
